package com.excel.util;

import cn.hutool.core.util.StrUtil;
import com.excel.config.MinioConfig;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Minio 文件存储
 *
 * @author food
 */
@Component
public class MinioUtils {

    @Resource
    private MinioConfig minioConfig;

    @Resource
    private MinioClient client;

    /**
     * Minio文件上传接口
     *
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    public String uploadFile(MultipartFile file) throws Exception {
        // 文件名
        String fileName = file.getOriginalFilename();
//        fileName = PinYinUtil.getAllPinyin(fileName);
        // 获取输入流
        InputStream inputStream = file.getInputStream();
        // 文件路径
        String filePath = createFilePath(fileName);
        System.out.println("文件路径：" + filePath);
        PutObjectArgs args = PutObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(filePath)
                .stream(inputStream, file.getSize(), -1)
                .contentType(file.getContentType())
                .build();
        client.putObject(args);
//        IoUtils.closeQuietly(inputStream);
//        return minioConfig.getUrl() + "/" + minioConfig.getBucketName() + "/" + fileName;
        return filePath;
    }



    /**
     * 获取文件路径
     *
     * @param fileName 原始文件名称
     * @return FilePath
     */
    public String createFilePath(String fileName) {
        UUID uuid = UUID.randomUUID();
        // 文件名前面加入UUID 防止服务器头像重复导致误删重复
        return new SimpleDateFormat("yyyy/MM/dd").format(new Date()) + "/" + uuid + "_" + fileName;
    }

    /**
     * 文件路径
     *
     * @param filePath 文件路径
     */
    public void getObject(HttpServletResponse httpServletResponse, String filePath) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
        String fileName = getFileName(filePath);
        InputStream inputStream = client.getObject(GetObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(filePath)
                .build());
        downloadFile(httpServletResponse, inputStream, fileName);
    }

    /**
     * 文件路径
     *
     * @param filePath 文件路径
     */
    public InputStream getObject(String filePath) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
        return client.getObject(GetObjectArgs.builder()
                .bucket(minioConfig.getBucketName())
                .object(filePath)
                .build());
    }

    /**
     * 根据文件路径获取文件名称
     *
     * @param filePath 文件路径
     * @return 文件名
     */
    public String getFileName(String filePath) {
        String[] split = StringUtils.split(filePath, "/");
        return split[split.length - 1];
    }


    // 删除文件：
    public void delete(String bucketName, String fileName) {
        /**
         * String bucketName = "test2";
         * String fileName = "/2023-04-07/16808560218465670_img.png";
         * address+bucketName+fileName 就是访问路径，删除需要后两个参数。
         */
        try {
            client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
        } catch (Exception e) {
            System.out.println("删除失败");
        }
        System.out.println("删除成功");
    }


    /**
     * 下载文件
     *
     * @param httpServletResponse httpServletResponse
     * @param inputStream         inputStream
     * @param fileName            文件名
     * @throws IOException IOException
     */
    public void downloadFile(HttpServletResponse httpServletResponse, InputStream inputStream, String fileName) throws IOException {
        // 设置响应头信息，告诉前端浏览器下载文件
        httpServletResponse.setContentType("application/octet-stream;charset=UTF-8");
        httpServletResponse.setCharacterEncoding("UTF-8");
        httpServletResponse.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
        // 获取输出流进行写入数据
        OutputStream outputStream = httpServletResponse.getOutputStream();
        // 将输入流复制到输出流
        byte[] buffer = new byte[4096];
        int bytesRead = -1;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            outputStream.write(buffer, 0, bytesRead);
        }
        // 关闭流资源
        inputStream.close();
        outputStream.close();
    }

    public String uploadApp(MultipartFile file) throws IOException, ServerException, InsufficientDataException, ErrorResponseException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String bucketName = minioConfig.getBucketName();

        String fileName = file.getOriginalFilename();
        String filePath = "upgrade/apk/" + fileName;
        long fileSize = file.getSize();
        String contentType = file.getContentType();

        try (InputStream is = file.getInputStream()) {
            PutObjectArgs args = PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(filePath)
                    .stream(is, fileSize, -1)
                    .contentType(contentType)
                    .build();
            client.putObject(args);
        }

        return filePath;
    }

    public String getServerAddress() {
        String serverAddress = minioConfig.getUrl() + "/";
        serverAddress += minioConfig.getBucketName() + "/";
        return serverAddress;
    }

    /**
     * 获取所有的文件块
     *
     * @param objectKey
     * @return
     */
    public Iterable<Result<Item>> listObjects(String objectKey) {
        return client.listObjects(
                ListObjectsArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .prefix(objectKey)
                        .build());
    }

    /**
     * 上传分片文件
     *
     * @param file
     * @param filePath
     * @throws Exception
     */
    public void uploadFileChunk(MultipartFile file, String filePath) throws Exception {
        try (InputStream inputStream = file.getInputStream()) {
            client.putObject(
                    PutObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(filePath)
                            .stream(inputStream, file.getSize(), -1)
                            .build());
        } catch (Exception e) {
            throw e;
        }
    }

    /**
     * 根据路径前缀查询是否数量齐全
     *
     * @param identifier
     * @param fileName
     * @param year
     * @param totalChunks
     * @return
     */
    public boolean chunkCount(String identifier, String fileName, String year, int totalChunks) throws Exception {

        int actualCount = 0;
        String filePrefix = getVideoTempPath(year, identifier, fileName);
        Iterable<Result<Item>> results = client.listObjects(
                ListObjectsArgs.builder()
                        .bucket(minioConfig.getBucketName())
                        .prefix(filePrefix)
                        .maxKeys(totalChunks) // 优化点1：限制最大返回数量
                        .recursive(true)
                        .build());

        for (Result<Item> result : results) {
            Item item = result.get();
            if (item != null && !item.isDir()) {
                actualCount++;
                // 优化点2：达到预期数量立即终止
                if (actualCount >= totalChunks) break;
            }
        }

        return actualCount == totalChunks;

    }

    public Map<String, String> mergeChunks(String identifier, String fileName, String year, int totalChunks, Map<String, String> result) throws Exception {

        final List<ComposeSource> sources = new ArrayList<>();

        String bucketName = minioConfig.getBucketName();
        String tempDir = getVideoTempPath(year, identifier, fileName);
        String mergeFilePath = getVideoMergePath(year, identifier, fileName);

        // 1. 构建分片源列表
        for (int i = 1; i <= totalChunks; i++) {
            String objectPath = tempDir + "/chunk-" + i;
            validateObjectPath(objectPath); // 路径校验

            sources.add(ComposeSource.builder()
                    .bucket(bucketName)
                    .object(objectPath)
                    .build());
        }

        // 2. 执行服务端合并
        client.composeObject(
                ComposeObjectArgs.builder()
                        .bucket(bucketName)
                        .object(mergeFilePath)
                        .sources(sources)
                        .build());

        // 3. 清理临时分片（带安全校验）
        List<DeleteObject> deleteObjects = sources.stream()
                .map(source -> {
                    String objectPath = source.object();
                    validateObjectPath(objectPath); // 二次校验
                    return new DeleteObject(objectPath);
                })
                .collect(Collectors.toList());

        if (!deleteObjects.isEmpty()) {
            client.removeObjects(
                    RemoveObjectsArgs.builder()
                            .bucket(bucketName)
                            .objects(deleteObjects)
                            .build());

            System.out.println("成功清理{" + deleteObjects.size() + "}个临时分片");
            result.put("minioClearTempFilePath", tempDir);
            result.put("minioClearTempFile", String.valueOf(deleteObjects.size()));
        }

        result.put("minioMergePath", mergeFilePath);

        return result;
    }

    // 路径校验方法
    private void validateObjectPath(String objectPath) {
        if (objectPath == null || objectPath.isEmpty()) {
            throw new IllegalArgumentException("对象路径不能为空");
        }
    }

    public String getVideoTempPath(String year, String identifier, String fileName) {
        String template = "video/{}/{}/temp/{}";
        return StrUtil.format(template, year, identifier, fileName);
    }

    public String getVideoMergePath(String year, String identifier, String fileName) {
        String template = "video/{}/{}/merge/{}";
        return StrUtil.format(template, year, identifier, fileName);
    }

    public String getVideoHlsPath(String year, String identifier, String fileName) {
        String template = "video/{}/{}/hls/{}";
        return StrUtil.format(template, year, identifier, fileName);
    }

}
